#include "PEIncludes.h"

#include "PEAliceSoftImageJpg.h"

#include "PEFile.h"
#include "PEImage.h"

namespace pe
{

	PE_EXPORT int AliceImageJpgReadHeader(AliceImageJpgHeader* a_Target, PEStream* a_Stream)
	{
		PEStreamRead(a_Stream, a_Target, sizeof(AliceImageJpgHeader), 1);

		if (a_Target->ident[0] != 'J' ||
			a_Target->ident[1] != 'F' ||
			a_Target->ident[2] != 'I' ||
			a_Target->ident[3] != 'F'
		)
		{
			return -1;
		}

		return PE_OK;
	}

	struct fiio_mem_handle 
	{
		long filelen, datalen, curpos;
		byte* data;
	};

	#define FIIOMEM(member) (((fiio_mem_handle*)a_Handle)->member)

	PE_EXPORT unsigned int fiio_mem_ReadProc(void* a_Buffer, unsigned int a_Size, unsigned int a_Count, fi_handle a_Handle) 
	{
		byte* dst = (byte*)a_Buffer;

		unsigned int x = 0;
		for (; x < a_Count; x++) 
		{
			//if there isn't size bytes left to read, set pos to eof and return a short count
			if (FIIOMEM(filelen) - FIIOMEM(curpos) < (long)a_Size) 
			{
				FIIOMEM(curpos) = FIIOMEM(filelen);
				break;
			}

			//copy size bytes count times
			memcpy(dst, (char*)FIIOMEM(data) + FIIOMEM(curpos), a_Size);
			FIIOMEM(curpos) += a_Size;
			//a_Buffer = (char*)a_Buffer + a_Size;
			dst += a_Size;
		}

		return x;
	}

	PE_EXPORT int fiio_mem_SeekProc(fi_handle a_Handle, long a_Offset, int a_Origin) 
	{
		//0 to filelen-1 are 'inside' the file
		switch (a_Origin) 
		{ 
		
		default:
		case SEEK_SET: //can fseek() to 0-7FFFFFFF always
			{
				if (a_Offset >= 0) 
				{
					FIIOMEM(curpos) = a_Offset;
					return 0;
				}

				break;
			}

		case SEEK_CUR:
			{
				if( FIIOMEM(curpos) + a_Offset >= 0) 
				{
					FIIOMEM(curpos) += a_Offset;
					return 0;
				}

				break;
			}

		case SEEK_END:
			{
				if (FIIOMEM(filelen) + a_Offset >= 0) 
				{
					FIIOMEM(curpos) = FIIOMEM(filelen) + a_Offset;
					return 0;
				}

				break;
			}
		}

		return -1;
	}

	PE_EXPORT long fiio_mem_TellProc(fi_handle a_Handle) 
	{
		return FIIOMEM(curpos);
	}

	PE_EXPORT unsigned int fiio_mem_WriteProc(void* a_Buffer, unsigned int a_Size, unsigned int a_Count, fi_handle a_Handle)
	{
		return 0;
	}

	void SetMemIO(FreeImageIO* a_InputOutput) 
	{
		a_InputOutput->read_proc  = (FI_ReadProc)fiio_mem_ReadProc;
		a_InputOutput->seek_proc  = (FI_SeekProc)fiio_mem_SeekProc;
		a_InputOutput->tell_proc  = (FI_TellProc)fiio_mem_TellProc;
		a_InputOutput->write_proc = (FI_WriteProc)fiio_mem_WriteProc;
	}

	PE_EXPORT int AliceImageJpgReadData(AliceImageJpg* a_Target, AliceImageJpgHeader* a_Header, PEStream* a_Stream)
	{
		a_Target->width = a_Header->width;
		a_Target->height = a_Header->height;

		PEStreamGetFileSize(a_Stream, &a_Target->data_length);
		a_Target->data_length += sizeof(AliceImageJpgHeader);

		PEStreamSeek(a_Stream, 0, SEEK_SET);

		a_Target->data = new byte[a_Target->data_length];
		PEStreamRead(a_Stream, a_Target->data, a_Target->data_length, 1);

		return PE_OK;
	}

	PE_EXPORT int AliceImageJpgToRaw(PEImageRaw* a_Target, AliceImageJpg* a_Image)
	{
		FreeImageIO io;
		SetMemIO(&io);

		fiio_mem_handle handle;
		handle.curpos = 0;
		handle.datalen = a_Image->data_length;
		handle.filelen = a_Image->data_length;
		handle.data = a_Image->data;

		fipMemoryIO img_stream(a_Image->data, a_Image->data_length);
		FREE_IMAGE_FORMAT fif = img_stream.getFileType();

		fipImage img;
		BOOL result = img.loadFromMemory(img_stream);
		unsigned int w = img.getWidth();
		unsigned int h = img.getHeight();
		FIBITMAP* dib = (FIBITMAP*)img;

		PEImageRawCreate(a_Target, img.getWidth(), img.getHeight());

		unsigned int dst_pitch = a_Target->width * 4;
		unsigned int src_pitch = img.getScanWidth();

		unsigned int pixels = 0;

		for (unsigned int y = a_Target->height - 1; y > 0; y--)
		{
			byte* src = (byte*)img.getScanLine(y);
			byte* dst = a_Target->data + (dst_pitch * y);

			for (unsigned int x = 0; x < a_Target->width; x++)
			{
				dst[0] = src[0];
				dst[1] = src[1];
				dst[2] = src[2];
				dst[3] = 255;

				dst += 4;
				src += 3;

				pixels++;
			}
		}

		return PE_OK;
	}

	PE_EXPORT int AliceImageJpgReadAndDecrypt(PEImageRaw* a_Target, PEStream* a_Stream)
	{
		PE_STUB();
		return PE_OK;
	}

	PE_EXPORT int AliceImageJpgDeleteHeader(AliceImageJpgHeader* a_Header)
	{
		PE_STUB();
		return PE_OK;
	}

	PE_EXPORT int AliceImageJpgDeleteData(AliceImageJpg* a_Image)
	{
		delete a_Image->data;

		return PE_OK;
	}

}